/*
 *  Board.cpp
 *  RiskAI
 *
 */

#include "Board.h"
#include "Player.h"

#include <iostream>
#include <fstream>

using namespace std;

//**********************************************************************************
//				CONSTRUCTION/DESTRUCTION
//**********************************************************************************

CBoard::CBoard(bool bPrintsVisible)
	: m_bPrintsVisible(bPrintsVisible)
{
}

CBoard::~CBoard()
{
}




//**********************************************************************************
//				SETUP
//**********************************************************************************

bool
CBoard::LoadFromFile(const char* sFileName)
{
	Clear();
	
	ifstream input(sFileName);
	if(input.is_open())
	{
		int iCounter = 0;
		while(true)
		{
			string sElemType;
			input >> sElemType;
			if(input.fail()) break;
			
			if(sElemType == "STARTING_ARMIES")
			{
				input >> m_iStartingArmies;
			}
			else if(sElemType == "TERRITORY")
			{				
				CTerritory* terr = new CTerritory(iCounter++);
				int numBorders;
				input >> numBorders;
				
				for(int i=0; i<numBorders; i++)
				{
					int border;
					input >> border;
					terr->AddBorder(border);
				}
			
				terr->SetOwner(-1);
				terr->SetNumArmies(0);
				
				m_vpxTerritories.push_back(terr);
			}
			else // sElemType == "CONTINENT"
			{				
				CContinent* cont = new CContinent(this);
				
				int iNumTerritories;
				input >> iNumTerritories;
								
				for(int i=0; i<iNumTerritories; i++)
				{
					int iTerritoryIndex;
					input >> iTerritoryIndex;
					cont->AddTerritory(iTerritoryIndex);
				}
				
				int iBonus;
				input >> iBonus;
				cont->SetBonus(iBonus);
				
				m_vpxContinents.push_back(cont);
			}
		}
		
		input.close();
		
		BuildDistMatrix();
		return true;
	}
	else
	{
		return false;
	}
}

//==================================================================================

void
CBoard::Clear()
{
	for(unsigned int i=0; i<m_vpxTerritories.size(); i++)
		delete m_vpxTerritories[i];
	m_vpxTerritories.clear();
	
	for(unsigned int i=0; i<m_vpxContinents.size(); i++)
		delete m_vpxContinents[i];
	m_vpxContinents.clear();
	
	m_vviDistances.clear();
	
	m_iStartingArmies = 0;
}

//==================================================================================

void
CBoard::WipeGame()
{
	for(unsigned int i=0; i<m_vpxTerritories.size(); i++)
	{
		m_vpxTerritories[i]->SetOwner(-1);
		m_vpxTerritories[i]->SetNumArmies(0);
	}
}

//==================================================================================

struct Node{
	int iIndex;
	int iDepth;
	
	Node(int i, int d){ iIndex = i; iDepth = d; }
};

void
CBoard::BuildDistMatrix()
{
	unsigned int iBoardSize = m_vpxTerritories.size();
	
	for(int i=0; i<iBoardSize; i++)
	{
		vector<int> viTemp;
		for(int j=0; j<iBoardSize; j++)
			viTemp.push_back(-1);
		m_vviDistances.push_back(viTemp);
	}
	
	for(unsigned int iRoot=0; iRoot<iBoardSize; iRoot++)
	{
		vector<Node> vxQueue;
		
		m_vviDistances[iRoot][iRoot] = 0;
		vxQueue.push_back(Node(iRoot, 0));
		
		// BFS the distance to every other territory
		while(!vxQueue.empty())
		{
			// manually pop front :(
			Node xCurr = vxQueue[0];
			vxQueue.erase(vxQueue.begin());
			
			vector<int> viBorders = m_vpxTerritories[xCurr.iIndex]->GetBorderIndices();
			for(unsigned int i=0; i<viBorders.size(); i++)
			{
				int iIndex = viBorders[i];
				
				if(m_vviDistances[iRoot][iIndex] == -1)
				{
					m_vviDistances[iRoot][iIndex] = xCurr.iDepth + 1;
					vxQueue.push_back(Node(iIndex, xCurr.iDepth + 1));
				}
			}
		}
	}
	
	// PrintDistMatrix();
}

//==================================================================================

void
CBoard::PrintDistMatrix()
{
	cout << "DISTANCES MATRIX:" << endl;
	for(unsigned int i=0; i<m_vviDistances.size(); i++)
	{
		for(unsigned int j=0; j<m_vviDistances[i].size(); j++)
		{
			cout << m_vviDistances[i][j] << " ";
			if(m_vviDistances[i][j] < 10) cout << " ";
		}
		cout << endl;
	}
}









//**********************************************************************************
//				CORE FUNCTIONALITY
//**********************************************************************************

CTerritory*
CBoard::GetTerritory(int iTerritoryIndex)
{
	return m_vpxTerritories[iTerritoryIndex];
}

//==================================================================================

int
CBoard::GetNumTerritories()
{
	return int(m_vpxTerritories.size());
}

//==================================================================================

void
CBoard::AssignRandomOwnership(vector<CPlayer*> vpxPlayers)
{
	vector<CTerritory*> vxRemainingTerritory = m_vpxTerritories;
	
	int iNumPlayers = int(vpxPlayers.size());
	int playerCounter = rand() % iNumPlayers;
	
	while(!vxRemainingTerritory.empty())
	{
		int iPlayer = playerCounter++ % iNumPlayers;
		int iIndex = rand() % vxRemainingTerritory.size();
				
		vxRemainingTerritory[iIndex]->SetOwner(iPlayer);
		vxRemainingTerritory[iIndex]->AddArmies(1);
		
		vpxPlayers[iPlayer]->GainTerritory(vxRemainingTerritory[iIndex]->GetIndex());
		
		vxRemainingTerritory.erase(vxRemainingTerritory.begin() + iIndex);
	}
}

//==================================================================================

void
CBoard::ChooseOwnership(vector<CPlayer*> vpxPlayers)
{
	vector<int> viRemainingTerritory;
	
	for(unsigned int i=0; i<m_vpxTerritories.size(); i++)
		viRemainingTerritory.push_back(i);
	
	int iNumPlayers = int(vpxPlayers.size());
	int playerCounter = rand() % iNumPlayers;
	
	while(!viRemainingTerritory.empty())
	{
		int iPlayer = playerCounter++ % iNumPlayers;
		int iIndex = vpxPlayers[iPlayer]->PickTerritory(viRemainingTerritory);
				
		m_vpxTerritories[iIndex]->SetOwner(iPlayer);
		m_vpxTerritories[iIndex]->AddArmies(1);
		
		vpxPlayers[iPlayer]->GainTerritory(iIndex);
		
		for(unsigned int i=0; i<viRemainingTerritory.size(); i++)
		{
			if(viRemainingTerritory[i] == iIndex)
			{
				viRemainingTerritory.erase(viRemainingTerritory.begin() + i);
				break;
			}
		}
	}
}

//==================================================================================

void
CBoard::PlaceStartingArmies(vector<CPlayer*> vpxPlayers, bool bRandom)
{
	for(int iTroopsLeft=m_iStartingArmies; iTroopsLeft>0; iTroopsLeft--)
	{
		for(unsigned int i=0; i<vpxPlayers.size(); i++)
		{
			int iIndex;
			if(bRandom)
			{
				vector<int> viPossibles = vpxPlayers[i]->GetTerritories();
				iIndex = viPossibles[rand() % viPossibles.size()];
			}
			else
			{
				iIndex = vpxPlayers[i]->AssignTroops(iTroopsLeft);
			}
			
			m_vpxTerritories[iIndex]->AddArmies(1);
		}
	}
}

//==================================================================================

bool 
CBoard::PlayerHasWon(CPlayer* pxPlayer)
{
	unsigned int iTerritories = pxPlayer->GetTerritories().size();
	return (iTerritories == m_vpxTerritories.size());
}

//==================================================================================

int
CBoard::CalculateProduction(vector<int> viTerritories)
{
	int iTroops = int(viTerritories.size()) / 3;
	if(iTroops < 3) iTroops = 3;
	
	int iPlayer = m_vpxTerritories[viTerritories[0]]->GetOwner();
	for(unsigned int i=0; i<m_vpxContinents.size(); i++)
		iTroops += m_vpxContinents[i]->GetBonusForPlayer(iPlayer);
	
	return iTroops;
}

//==================================================================================

int
CBoard::EnemiesSurroundingTerritory(int iIndex)
{
	CTerritory* terr = m_vpxTerritories[iIndex];
	int iNumEnemies = 0;
	
	for(unsigned int j=0; j<terr->GetBorderIndices().size(); j++)
	{
		CTerritory* border = m_vpxTerritories[terr->GetBorderIndices()[j]];
		if(border->GetOwner() != terr->GetOwner())
			iNumEnemies += border->GetNumArmies();
	}
	
	return iNumEnemies;
}

//==================================================================================

vector<int>
CBoard::GetViableAttackers(CPlayer* pxPlayer)
{
	vector<int> viViableAttackers;
	
	int iPlayerIndex = pxPlayer->GetIndex();
	
	for(unsigned int i=0; i<pxPlayer->GetTerritories().size(); i++)
	{
		CTerritory* terr = m_vpxTerritories[pxPlayer->GetTerritories()[i]];
		if(terr->GetNumArmies() > 1)
		{
			for(unsigned int j=0; j<terr->GetBorderIndices().size(); j++)
			{
				CTerritory* border = m_vpxTerritories[terr->GetBorderIndices()[j]];
				if(border->GetOwner() != iPlayerIndex)
				{
					viViableAttackers.push_back(pxPlayer->GetTerritories()[i]);
					break;
				}
			}
		}
	}
	
	return viViableAttackers;
}

//==================================================================================

vector<int>
CBoard::GetViableFortifiers(CPlayer* pxPlayer)
{
	vector<int> viFortifiers;
	
	int iPlayerIndex = pxPlayer->GetIndex();
	
	for(unsigned int i=0; i<pxPlayer->GetTerritories().size(); i++)
	{
		CTerritory* terr = m_vpxTerritories[pxPlayer->GetTerritories()[i]];
		if(terr->GetNumArmies() > 1)
		{
			for(unsigned int j=0; j<terr->GetBorderIndices().size(); j++)
			{
				CTerritory* border = m_vpxTerritories[terr->GetBorderIndices()[j]];
				if(border->GetOwner() == iPlayerIndex)
				{
					viFortifiers.push_back(pxPlayer->GetTerritories()[i]);
					break;
				}
			}
		}
	}
	
	return viFortifiers;
}






//**********************************************************************************
//				PRINTING
//**********************************************************************************

void
CBoard::PrintBoardState(bool bDebug)
{
	if(m_bPrintsVisible || bDebug)
	{
		cout << endl << "Current Board State:" << endl;
		for(unsigned int i=0; i<m_vpxTerritories.size(); i++)
		{
			cout << m_vpxTerritories[i]->ToString() << endl;
		}
	}
}

//==================================================================================

std::string
CBoard::GetTerritoryInfo(int iIndex)
{
	return m_vpxTerritories[iIndex]->ToString();
}